package com.eco.fanliapp.service;

import cn.jiguang.common.utils.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * @描述：Redis 做分布式锁，利用 redis的setnx（SET if Not eXists）命令 和 getset 命令，即下面代码中的方法 setIfAbsent 和 getAndSet
 * @作者：liuchong
 * @时间：2019年08月16日 下午5:21
 * @版本：
 */
@Component
public class RedisLockService {

    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 加锁
     * @param key 被秒杀商品的id
     * @param value 当前线程操作时的 System.currentTimeMillis() + 2000，2000是超时时间，这个地方不需要去设置redis的expire，
     *              也不需要超时后手动去删除该key，因为会存在并发的线程都会去删除，造成上一个锁失效，结果都获得锁去执行，并发操作失败了就。
     * @return
     */
    public boolean lock(String key, String value) {
        try {
            //如果key值不存在，则返回 true，且设置 value
            if (stringRedisTemplate.opsForValue().setIfAbsent(key, value)) {
                return true;
            }
            //获取key的值，判断是是否超时
            Object curValObj = null;
            try {
                curValObj = stringRedisTemplate.opsForValue().get(key);
            } catch (Exception e) {
                curValObj = stringRedisTemplate.opsForValue().get(key);
            }

            String curVal = curValObj == null ? null : curValObj.toString().replaceAll("\"", "");
            if (StringUtils.isNotEmpty(curVal) && Long.parseLong(curVal) < System.currentTimeMillis()) {
                //获得之前的key值，同时设置当前的传入的value。这个地方可能几个线程同时过来，但是redis本身天然是单线程的，所以getAndSet方法还是会安全执行，
                //首先执行的线程，此时curVal当然和oldVal值相等，因为就是同一个值，之后该线程set了自己的value，后面的线程就取不到锁了
                Object oldValObj = stringRedisTemplate.opsForValue().getAndSet(key, value);
                String oldVal = oldValObj == null ? null : oldValObj.toString().replaceAll("\"", "");
                if(StringUtils.isNotEmpty(oldVal) && oldVal.equals(curVal)) {
                    return true;
                }
            }
            stringRedisTemplate.expire(key, 60 * 15, TimeUnit.SECONDS);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 解锁
     * @param key
     * @param value
     */
    public void unlock(String key, String value) {
        try {
//            String curVal = stringRedisTemplate.opsForValue().get(key);
			stringRedisTemplate.opsForValue().getOperations().delete(key);
//            if (StringUtils.isNotEmpty(curVal) && curVal.equals(value)) {
//				stringRedisTemplate.opsForValue().getOperations().delete(key);
//            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        Object s = "1574413590357\"";
    }
}
